﻿namespace FileCategorizerTest
{
    using System;
    using System.Collections.Specialized;
    using System.IO;
    using FileCategorizer.Engine;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Unit tests for the categorizer class.
    /// </summary>
    [TestClass]
    public class CategorizerTest
    {
        /// <summary>
        /// The testing directory.
        /// </summary>
        private static string testDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

        /// <summary>
        /// This test ensures that multiple calls to the static instance variable
        /// are returning the *same* object.  It is not sufficient to return equal
        /// objects, there can only be one Categorizer during the life time of the
        /// application.
        /// </summary>
        [TestMethod]
        public void CategorizerInstantiation()
        {
            Categorizer c1 = Categorizer.Instance;
            Categorizer c2 = Categorizer.Instance;

            Assert.IsNotNull(c1);
            Assert.IsNotNull(c2);
            Assert.AreSame(c1, c2);
        }

        /// <summary>
        /// Tests the categorizers ability to properly assign the requested
        /// category to the file.
        /// </summary>
        [TestMethod]
        public void CategorizeSingleFile()
        {
            string fullPath = Path.Combine(testDir, "temp.txt");
            string tag = "test";

            Categorizer c = Categorizer.Instance;
            c.Categorize(fullPath, tag);
            Assert.IsTrue(c.HasTag(fullPath, tag));
        }

        /// <summary>
        /// Tests the categorizer's ability to tag multiple files.
        /// </summary>
        [TestMethod]
        public void CategorizeMultipleFiles()
        {
            Categorizer c = Categorizer.Instance;

            string tag = "test";
            string[] files = new string[5];
            for (int x = 0; x < 5; x++)
            {
                files[x] = Path.Combine(testDir, String.Format("temp{0}.txt", x));
            }

            foreach (string s in files)
            {
                c.Categorize(s, tag);
            }

            foreach (string s in files)
            {
                Assert.IsTrue(c.HasTag(s, tag));
            }
        }

        /// <summary>
        /// Tests whether the categorizer properly handles multiple files at
        /// the same time with varying tags.
        /// </summary>
        [TestMethod]
        public void CategorizeMultipleFilesWithMultipleTags()
        {
            Categorizer c = Categorizer.Instance;

            c.Categorize(Path.Combine(testDir, "temp1.txt"), "tag1");
            c.Categorize(Path.Combine(testDir, "temp1.txt"), "tag2");
            c.Categorize(Path.Combine(testDir, "temp2.txt"), "tag3");
            c.Categorize(Path.Combine(testDir, "temp2.txt"), "tag4");
            c.Categorize(Path.Combine(testDir, "temp3.txt"), "tag5");
            c.Categorize(Path.Combine(testDir, "temp3.txt"), "tag1");
            c.Categorize(Path.Combine(testDir, "temp4.txt"), "tag2");
            c.Categorize(Path.Combine(testDir, "temp4.txt"), "tag3");
            c.Categorize(Path.Combine(testDir, "temp5.txt"), "tag4");
            c.Categorize(Path.Combine(testDir, "temp5.txt"), "tag5");

            Assert.IsFalse(c.IsEmpty);

            Assert.IsTrue(c.HasTag(Path.Combine(testDir, "temp1.txt"), "tag1"));
            Assert.IsTrue(c.HasTag(Path.Combine(testDir, "temp1.txt"), "tag2"));
            Assert.IsTrue(c.HasTag(Path.Combine(testDir, "temp2.txt"), "tag3"));
            Assert.IsTrue(c.HasTag(Path.Combine(testDir, "temp2.txt"), "tag4"));
            Assert.IsTrue(c.HasTag(Path.Combine(testDir, "temp3.txt"), "tag5"));
            Assert.IsTrue(c.HasTag(Path.Combine(testDir, "temp3.txt"), "tag1"));
            Assert.IsTrue(c.HasTag(Path.Combine(testDir, "temp4.txt"), "tag2"));
            Assert.IsTrue(c.HasTag(Path.Combine(testDir, "temp4.txt"), "tag3"));
            Assert.IsTrue(c.HasTag(Path.Combine(testDir, "temp5.txt"), "tag4"));
            Assert.IsTrue(c.HasTag(Path.Combine(testDir, "temp5.txt"), "tag5"));

            c.Clear();
            Assert.IsTrue(c.IsEmpty);
        }

        /// <summary>
        /// Adds files to the categorizer, then attempts to locate a
        /// specific file by name.
        /// </summary>
        [TestMethod]
        public void SearchForFileByName()
        {
            Categorizer c = Categorizer.Instance;

            c.Clear();
            Assert.IsTrue(c.IsEmpty);

            c.Categorize(Path.Combine(testDir, "temp1.txt"), "tag1");
            c.Categorize(Path.Combine(testDir, "temp1.txt"), "tag2");
            c.Categorize(Path.Combine(testDir, "temp2.txt"), "tag3");
            c.Categorize(Path.Combine(testDir, "temp2.txt"), "tag4");
            c.Categorize(Path.Combine(testDir, "temp3.txt"), "tag5");
            c.Categorize(Path.Combine(testDir, "temp3.txt"), "tag1");
            c.Categorize(Path.Combine(testDir, "temp4.txt"), "tag2");
            c.Categorize(Path.Combine(testDir, "temp4.txt"), "tag3");
            c.Categorize(Path.Combine(testDir, "temp5.txt"), "tag4");
            c.Categorize(Path.Combine(testDir, "temp5.txt"), "tag5");

            Assert.IsFalse(c.IsEmpty);

            StringCollection expected = new StringCollection { Path.Combine(testDir, "temp3.txt") };
            StringCollection actual = c.SearchByName("temp3.txt");
            CollectionAssert.AreEquivalent(expected, actual);

            c.Clear();
            Assert.IsTrue(c.IsEmpty);
        }

        /// <summary>
        /// This test effectively does what SearchForFileByName does, but
        /// generalizes the search criteria to ensure we only get distinct
        /// file names back from the database.
        /// </summary>
        [TestMethod]
        public void SearchForFileByNameMultiple()
        {
            Categorizer c = Categorizer.Instance;

            c.Clear();
            Assert.IsTrue(c.IsEmpty);

            c.Categorize(Path.Combine(testDir, "temp1.txt"), "tag1");
            c.Categorize(Path.Combine(testDir, "temp1.txt"), "tag2");
            c.Categorize(Path.Combine(testDir, "temp2.txt"), "tag3");
            c.Categorize(Path.Combine(testDir, "temp2.txt"), "tag4");
            c.Categorize(Path.Combine(testDir, "temp3.txt"), "tag5");
            c.Categorize(Path.Combine(testDir, "temp3.txt"), "tag1");
            c.Categorize(Path.Combine(testDir, "temp4.txt"), "tag2");
            c.Categorize(Path.Combine(testDir, "temp4.txt"), "tag3");
            c.Categorize(Path.Combine(testDir, "temp5.txt"), "tag4");
            c.Categorize(Path.Combine(testDir, "temp5.txt"), "tag5");

            Assert.IsFalse(c.IsEmpty);

            StringCollection expected = new StringCollection();
            expected.Add(Path.Combine(testDir, "temp1.txt"));
            expected.Add(Path.Combine(testDir, "temp2.txt"));
            expected.Add(Path.Combine(testDir, "temp3.txt"));
            expected.Add(Path.Combine(testDir, "temp4.txt"));
            expected.Add(Path.Combine(testDir, "temp5.txt"));

            StringCollection actual = c.SearchByName("temp");
            CollectionAssert.AreEquivalent(expected, actual);

            c.Clear();
            Assert.IsTrue(c.IsEmpty);
        }

        /// <summary>
        /// Adds files to the categorizer, then attempts to locate them
        /// by passing a specific tag.
        /// </summary>
        [TestMethod]
        public void SearchForFilesByTag()
        {
            Categorizer c = Categorizer.Instance;

            c.Clear();
            Assert.IsTrue(c.IsEmpty);

            c.Categorize(Path.Combine(testDir, "temp1.txt"), "tag1");
            c.Categorize(Path.Combine(testDir, "temp1.txt"), "tag2");
            c.Categorize(Path.Combine(testDir, "temp2.txt"), "tag3");
            c.Categorize(Path.Combine(testDir, "temp2.txt"), "tag4");
            c.Categorize(Path.Combine(testDir, "temp3.txt"), "tag5");
            c.Categorize(Path.Combine(testDir, "temp3.txt"), "tag1");
            c.Categorize(Path.Combine(testDir, "temp4.txt"), "tag2");
            c.Categorize(Path.Combine(testDir, "temp4.txt"), "tag3");
            c.Categorize(Path.Combine(testDir, "temp5.txt"), "tag4");
            c.Categorize(Path.Combine(testDir, "temp5.txt"), "tag5");

            Assert.IsFalse(c.IsEmpty);

            StringCollection expected = new StringCollection();
            expected.Add(Path.Combine(testDir, "temp2.txt"));
            expected.Add(Path.Combine(testDir, "temp5.txt"));

            string tag = "tag4";
            StringCollection actual = c.SearchByTag(tag);
            CollectionAssert.AreEquivalent(expected, actual);

            c.Clear();
            Assert.IsTrue(c.IsEmpty);
        }
    }
}
